//
//  ZYThreadBacktrace.h
//  ZYWebImage
//
//  Created by wangzhipan on 2025/5/7.
//

#import <Foundation/Foundation.h>
#import <mach/mach.h>

/*
 1、atos -arch arm64 -o MyApp.app/MyApp -l loadAddress AddressesToSymbolicate
 
    loadAddress：app加载到内存中的地址（有随机偏移地址）
    AddressesToSymbolicate： 崩溃发生的地址
 
    一般情况下，相对偏移地址 == AddressesToSymbolicate - loadAddress
 
        loadAddress 是应用程序被加载到内存中的基地址。但有几点需要补充：

             a. 默认基地址
    
                - 如果没有 ASLR（地址空间布局随机化），默认是 0x100000000
                - 这是 iOS/macOS 系统中 Mach-O 可执行文件的默认加载地址
             b. ASLR 影响
                
                - 现代 iOS 系统默认开启 ASLR
                - 每次启动时，系统会随机偏移这个基地址
                - 实际加载地址 = 0x100000000 + 随机偏移量
 
 2、atos -arch arm64 -o MyApp.app/MyApp errorAddress
 
    errorAddress = 崩溃地址 - ASLR偏移量
 
    实际计算：
        errorAddress = 默认基地址（0x100000000） +  崩溃地址（AddressesToSymbolicate） - app加载到内存中的地址(loadAddress)
 
 3、为什么MetricKit能捕获到crashSdk捕获不到的watchdog崩溃，举例说明：

     a、系统权限不一样，MetricKit是官方框架具有系统权限，即使应用被立马强杀也能保存数据
     b、crashSDK运行在应用层，无法获取系统级的终止信息，依赖于app存活，系统强杀时无法执行异常捕获逻辑

     c、MetricKit可以监控系统资源使用情况，而crashSDK主要关注应用层本app的资源使用

 4、iOS 系统资源管理和应用强杀机制详解：

              a. 首先清理后台应用
             
                     - 系统会优先强杀后台应用（如示例中的 App1 和 App2）
                     - 后台应用的强杀顺序基于多个因素：
                       - 后台驻留时间
                       - 内存占用量
                       - CPU 使用历史
                       - 用户使用频率
              b. 前台应用处理
             
                     - 如果清理后台应用后资源仍然紧张
                     - 系统会考虑强杀前台应用（如示例中的 App3）

          ## 详细的强杀决策机制
              c. 资源监控阈值
             
                     - CPU 使用率阈值：通常是 80%
                     - 内存压力等级监控
                     - 设备温度监控
                     - 能耗监控
              d. 应用优先级
             
                     - 前台活跃应用：最高优先级
                     - 前台暂停应用：次高优先级
                     - 后台最近使用：中等优先级
                     - 后台长期未用：低优先级
                     - 后台异常（如 CPU/内存异常）：最低优先级
              e. 强杀触发条件
             
                     - 系统资源紧张（CPU、内存、温度等）
                     - 应用行为异常（如 CPU 使用过高）
                     - 系统需要释放资源
 
 
 5、react native原理：
 
    -js层： 编写的react/js 代码运行层
    -bridge层： js与native通信
    -native层： 原生界面创建、UI渲染
 
    a、JS线程
        - 运行react/js 代码
        - 处理业务逻辑
        - 生成虚拟DOM树
 
    b、原生线程：
        - 运行原生UI组件，比如view的创建
        - 处理原生api调用
        - 渲染实际界面
 
    c、bridge层：
        - 负责JavaScript和原生代码之间的通信
        - JavaScript调用原生API时，消息通过Bridge传递给原生模块
        - 原生事件发生时，通过Bridge传递给JavaScript处理
        - 通信是异步的
 
    d、渲染流程：
        - js代码通过react构建虚拟DOM树
        - 虚拟DOM树的变化通过bridge传给native
        - native端根据这些指令创建或更新原生视图
        - 用户交互事件从原生端通过Bridge传回JavaScript处理
 
 
 # iOS 应用加载流程详解
 iOS 应用的启动过程是一个复杂的流程，从用户点击应用图标到应用界面显示出来，经历了多个阶段。下面详细介绍这个过程：

 ## 1. 点击应用图标
     当用户点击应用图标时，iOS 系统会启动这个应用的进程。

 ## 2. dyld 加载阶段
 dyld (动态链接器) 是 iOS 系统中负责加载可执行文件的组件，它的工作包括：

     1. 加载可执行文件 ：将 Mach-O 格式的可执行文件加载到内存中
     2. 加载动态库 ：加载应用依赖的所有动态库，包括系统库和第三方库
     3. 链接 ：处理符号表，将代码中的符号引用与实际地址进行链接
     4. 初始化 ：调用每个库的初始化函数
     dyld 3 (iOS 13 及以上版本) 引入了共享缓存机制，可以显著提高加载速度。

 ## 3. Runtime 初始化
 在 dyld 完成基本加载后，Objective-C runtime 系统开始初始化：

     1. 注册所有 Objective-C 类
     2. 处理类的继承关系
     3. 处理 Category 扩展
     4. 初始化类的 +load 方法 ：按照继承层次和编译顺序调用
 ## 4. UIKit 初始化
 UIKit 框架开始初始化，主要步骤包括：

     1. 创建 UIApplication 对象
     2. 设置 AppDelegate
     3. 调用 application:didFinishLaunchingWithOptions: 方法
 ## 5. 图像资源加载
 应用启动过程中的图像资源加载主要包括：

     1. LaunchScreen 加载 ：首先显示启动屏幕，给用户快速反馈
     2. Asset Catalog 处理 ：从 Asset Catalog 中加载图像资源
     3. 图像解码 ：将压缩的图像文件解码为可以直接渲染的位图
     4. 缓存处理 ：系统会对图像进行缓存以提高性能
     ## 6. 主界面加载
     1. 创建主窗口 (UIWindow)
     2. 加载根视图控制器
     3. 视图层次构建
     4. 布局计算
     5. 渲染准备
 ## 7. 首次渲染
     1. Core Animation 准备 ：准备图层树
     2. GPU 渲染 ：将图层内容提交给 GPU 进行渲染
     3. 显示到屏幕 ：最终内容显示在设备屏幕上

 5、app启动加载流程
 
 6、textKit 绘制计算
 
 7、socekt画板实现思路
 */

/**
 * 线程堆栈捕获工具类
 * 用于在崩溃发生时挂起所有线程并捕获堆栈信息
 */
NS_ASSUME_NONNULL_BEGIN

@interface ZYThreadBacktrace : NSObject
/**
 * 挂起除当前线程外的所有线程
 * @return 挂起的线程数组
 */
+ (NSArray <NSNumber *> *)suspendAllThreadsExceptCaller;

/**
 * 恢复之前挂起的线程
 * @param threads 之前挂起的线程数组
 */
+ (void)resumeThreads:(NSArray<NSNumber *> *)threads;

/**
 * 获取所有线程的堆栈信息
 * @param isSkipCurrent 是否跳过当前线程
 * @return 包含所有线程堆栈的字典
 */
+ (NSDictionary *)captureAllThreadsBacktrace:(BOOL)isSkipCurrent;

/**
 * 获取指定线程的堆栈信息
 * @param thread 目标线程
 * @return 线程堆栈信息
 */
+ (NSArray <NSDictionary *> *)backtraceOfThread:(thread_t)thread;

/**
 * 获取当前线程的堆栈信息
 * @return 当前线程堆栈信息
 */
+ (NSArray <NSDictionary *> *)backtraceOfCurrentThreadUsingBacktraceAPI;

/**
 * 将堆栈信息格式化为可读字符串
 * @param backtrace 堆栈信息数组
 * @return 格式化后的字符串
 */
+ (NSString *)formatBacktrace:(NSArray<NSDictionary *> *)backtrace;

/**
 * 将线程信息格式化为可读字符串
 * @param threadInfo 线程信息字典
 * @return 格式化后的字符串
 */
+ (NSString *)formatThreadInfo:(NSDictionary *)threadInfo;

/**
 * 检查线程是否被挂起
 * @param thread 目标线程
 * @return 如果线程被挂起返回YES，否则返回NO
 */
+ (BOOL)isThreadSuspend:(thread_t)thread;

/**
 * 获取线程状态的描述
 * @param state 线程运行状态
 * @return 状态描述字符串
 */
+ (NSString *)descriptionForThreadState:(integer_t)state;
@end

NS_ASSUME_NONNULL_END
